home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / pyatspi / utils.py < prev   
Text File  |  2009-10-19  |  16KB  |  526 lines

  1. '''
  2. Utility functions for AT-SPI for querying interfaces, searching the hierarchy,
  3. converting constants to strings, and so forth.
  4.  
  5. @author: Peter Parente
  6. @organization: IBM Corporation
  7. @copyright: Copyright (c) 2005, 2007 IBM Corporation
  8. @license: LGPL
  9.  
  10. This library is free software; you can redistribute it and/or
  11. modify it under the terms of the GNU Library General Public
  12. License as published by the Free Software Foundation; either
  13. version 2 of the License, or (at your option) any later version.
  14.  
  15. This library is distributed in the hope that it will be useful,
  16. but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18. Library General Public License for more details.
  19.  
  20. You should have received a copy of the GNU Library General Public
  21. License along with this library; if not, write to the
  22. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  23. Boston, MA 02111-1307, USA.
  24.  
  25. Portions of this code originally licensed and copyright (c) 2005, 2007
  26. IBM Corporation under the BSD license, available at
  27. U{http://www.opensource.org/licenses/bsd-license.php}
  28. '''
  29. import ORBit
  30. import Accessibility__POA
  31.  
  32. def getInterfaceIID(obj):
  33.   '''
  34.   Gets the ID of an interface class or object in string format for use in
  35.   queryInterface.
  36.   
  37.   @param obj: Class representing an AT-SPI interface or instance
  38.   @type obj: object
  39.   @return: IID for the interface
  40.   @rtype: string
  41.   @raise AttributeError: When the parameter does not provide typecode info
  42.   '''
  43.   return obj.__typecode__.repo_id
  44.  
  45. def getInterfaceName(obj):
  46.   '''
  47.   Gets the human readable name of an interface class or object in string
  48.   format.
  49.   
  50.   @param obj: Class representing an AT-SPI interface or instance
  51.   @type obj: class
  52.   @return: Name of the interface
  53.   @rtype: string
  54.   @raise AttributeError: When the parameter does not provide typecode info
  55.   '''
  56.   return obj.__typecode__.name
  57.  
  58. # we're importing here to avoid cyclic importants; constants relies on the
  59. # two functions above
  60. import constants
  61.  
  62. def listInterfaces(obj):
  63.   '''
  64.   Gets a list of the names of all interfaces supported by this object. The
  65.   names are the short-hand interface names like "Accessible" and "Component",
  66.   not the full interface identifiers.
  67.  
  68.   @param obj: Arbitrary object to query for all accessibility related
  69.   interfaces. Must provide a queryInterface method.
  70.   @type obj: object
  71.   @return: Set of supported interface names
  72.   @rtype: set
  73.   @raise AttributeError: If the object provide does not implement
  74.   queryInterface
  75.   '''
  76.   names = set()
  77.   for ic in constants.ALL_INTERFACES:
  78.     io = obj.queryInterface(getInterfaceIID(ic))
  79.     if io is None:
  80.       continue
  81.     names.add(getInterfaceName(ic))
  82.   return names
  83.  
  84. def stringToConst(prefix, suffix):
  85.   '''
  86.   Maps a string name to an AT-SPI constant. The rules for the mapping are as 
  87.   follows:
  88.     - The prefix is captalized and has an _ appended to it.
  89.     - All spaces in the suffix are mapped to the _ character. 
  90.     - All alpha characters in the suffix are mapped to their uppercase.
  91.     
  92.   The resulting name is used with getattr to look up a constant with that name
  93.   in the L{constants} module. If such a constant does not exist, the string
  94.   suffix is returned instead.
  95.  
  96.   This method allows strings to be used to refer to roles, relations, etc.
  97.   without direct access to the constants. It also supports the future expansion
  98.   of roles, relations, etc. by allowing arbitrary strings which may or may not
  99.   map to the current standard set of roles, relations, etc., but may still
  100.   match some non-standard role, relation, etc. being reported by an
  101.   application.
  102.   
  103.   @param prefix: Prefix of the constant name such as role, relation, state, 
  104.     text, modifier, key
  105.   @type prefix: string
  106.   @param suffix: Name of the role, relation, etc. to use to lookup the constant
  107.   @type suffix: string
  108.   @return: The matching constant value
  109.   @rtype: object
  110.   '''
  111.   name = prefix.upper()+'_'+suffix.upper().replace(' ', '_')
  112.   return getattr(constants, name, suffix)
  113.  
  114. def stateToString(value):
  115.   '''
  116.   Converts a state value to a string based on the name of the state constant in
  117.   the L{constants} module that has the given value.
  118.   
  119.   @param value: An AT-SPI state
  120.   @type value: Accessibility.StateType
  121.   @return: Human readable, untranslated name of the state
  122.   @rtype: string
  123.   '''
  124.   return constants.STATE_VALUE_TO_NAME.get(value)
  125.  
  126. def relationToString(value):
  127.   '''
  128.   Converts a relation value to a string based on the name of the state constant
  129.   in the L{constants} module that has the given value.
  130.   
  131.   @param value: An AT-SPI relation
  132.   @type value: Accessibility.RelationType
  133.   @return: Human readable, untranslated name of the relation
  134.   @rtype: string
  135.   '''
  136.   return constants.RELATION_VALUE_TO_NAME.get(value)
  137.  
  138. def allModifiers():
  139.   '''
  140.   Generates all possible keyboard modifiers for use with 
  141.   L{registry.Registry.registerKeystrokeListener}.
  142.   '''
  143.   mask = 0
  144.   while mask <= (1 << constants.MODIFIER_NUMLOCK):
  145.     yield mask
  146.     mask += 1
  147.  
  148. def findDescendant(acc, pred, breadth_first=False):
  149.   '''
  150.   Searches for a descendant node satisfying the given predicate starting at 
  151.   this node. The search is performed in depth-first order by default or
  152.   in breadth first order if breadth_first is True. For example,
  153.   
  154.   my_win = findDescendant(lambda x: x.name == 'My Window')
  155.   
  156.   will search all descendants of x until one is located with the name 'My
  157.   Window' or all nodes are exausted. Calls L{_findDescendantDepth} or
  158.   L{_findDescendantBreadth} to start the recursive search.
  159.   
  160.   @param acc: Root accessible of the search
  161.   @type acc: Accessibility.Accessible
  162.   @param pred: Search predicate returning True if accessible matches the 
  163.       search criteria or False otherwise
  164.   @type pred: callable
  165.   @param breadth_first: Search breadth first (True) or depth first (False)?
  166.   @type breadth_first: boolean
  167.   @return: Accessible matching the criteria or None if not found
  168.   @rtype: Accessibility.Accessible or None
  169.   '''
  170.   if breadth_first:
  171.     return _findDescendantBreadth(acc, pred)
  172.  
  173.   for child in acc:
  174.     try:
  175.       ret = _findDescendantDepth(acc, pred)
  176.     except Exception:
  177.       ret = None
  178.     if ret is not None: return ret
  179.  
  180. def _findDescendantBreadth(acc, pred):
  181.   '''    
  182.   Internal function for locating one descendant. Called by L{findDescendant} to
  183.   start the search.
  184.   
  185.   @param acc: Root accessible of the search
  186.   @type acc: Accessibility.Accessible
  187.   @param pred: Search predicate returning True if accessible matches the 
  188.       search criteria or False otherwise
  189.   @type pred: callable
  190.   @return: Matching node or None to keep searching
  191.   @rtype: Accessibility.Accessible or None
  192.   '''
  193.   for child in acc:
  194.     try:
  195.       if pred(child): return child
  196.     except Exception:
  197.       pass
  198.   for child in acc:
  199.     try:
  200.       ret = _findDescendantBreadth(child, pred)
  201.     except Exception:
  202.       ret = None
  203.     if ret is not None: return ret
  204.  
  205. def _findDescendantDepth(acc, pred):
  206.   '''
  207.   Internal function for locating one descendant. Called by L{findDescendant} to
  208.   start the search.
  209.  
  210.   @param acc: Root accessible of the search
  211.   @type acc: Accessibility.Accessible
  212.   @param pred: Search predicate returning True if accessible matches the 
  213.     search criteria or False otherwise
  214.   @type pred: callable
  215.   @return: Matching node or None to keep searching
  216.   @rtype: Accessibility.Accessible or None
  217.   '''
  218.   try:
  219.     if pred(acc): return acc
  220.   except Exception:
  221.     pass
  222.   for child in acc:
  223.     try:
  224.       ret = _findDescendantDepth(child, pred)
  225.     except Exception:
  226.       ret = None
  227.     if ret is not None: return ret
  228.     
  229. def findAllDescendants(acc, pred):
  230.   '''
  231.   Searches for all descendant nodes satisfying the given predicate starting at 
  232.   this node. Does an in-order traversal. For example,
  233.   
  234.   pred = lambda x: x.getRole() == pyatspi.ROLE_PUSH_BUTTON
  235.   buttons = pyatspi.findAllDescendants(node, pred)
  236.   
  237.   will locate all push button descendants of node.
  238.   
  239.   @param acc: Root accessible of the search
  240.   @type acc: Accessibility.Accessible
  241.   @param pred: Search predicate returning True if accessible matches the 
  242.       search criteria or False otherwise
  243.   @type pred: callable
  244.   @return: All nodes matching the search criteria
  245.   @rtype: list
  246.   '''
  247.   matches = []
  248.   _findAllDescendants(acc, pred, matches)
  249.   return matches
  250.  
  251. def _findAllDescendants(acc, pred, matches):
  252.   '''
  253.   Internal method for collecting all descendants. Reuses the same matches
  254.   list so a new one does not need to be built on each recursive step.
  255.   '''
  256.   for child in acc:
  257.     try:
  258.       if pred(child): matches.append(child)
  259.     except Exception:
  260.       pass
  261.     _findAllDescendants(child, pred, matches)
  262.   
  263. def findAncestor(acc, pred):
  264.   '''
  265.   Searches for an ancestor satisfying the given predicate. Note that the
  266.   AT-SPI hierarchy is not always doubly linked. Node A may consider node B its
  267.   child, but B is not guaranteed to have node A as its parent (i.e. its parent
  268.   may be set to None). This means some searches may never make it all the way
  269.   up the hierarchy to the desktop level.
  270.   
  271.   @param acc: Starting accessible object
  272.   @type acc: Accessibility.Accessible
  273.   @param pred: Search predicate returning True if accessible matches the 
  274.     search criteria or False otherwise
  275.   @type pred: callable
  276.   @return: Node matching the criteria or None if not found
  277.   @rtype: Accessibility.Accessible
  278.   '''
  279.   if acc is None:
  280.     # guard against bad start condition
  281.     return None
  282.   while 1:
  283.     if acc.parent is None:
  284.       # stop if there is no parent and we haven't returned yet
  285.       return None
  286.     try:
  287.       if pred(acc.parent): return acc.parent
  288.     except Exception:
  289.       pass
  290.     # move to the parent
  291.     acc = acc.parent
  292.  
  293. def getPath(acc):
  294.   '''
  295.   Gets the path from the application ancestor to the given accessible in
  296.   terms of its child index at each level.
  297.   
  298.   @param acc: Target accessible
  299.   @type acc: Accessibility.Accessible
  300.   @return: Path to the target
  301.   @rtype: list of integer
  302.   @raise LookupError: When the application accessible cannot be reached
  303.   '''
  304.   path = []
  305.   while 1:
  306.     if acc.parent is None:
  307.       path.reverse()
  308.       return path
  309.     try:
  310.       path.append(acc.getIndexInParent())
  311.     except Exception:
  312.       raise LookupError
  313.     acc = acc.parent
  314.  
  315. class _StateSetImpl(Accessibility__POA.StateSet):
  316.   '''
  317.   Implementation of the StateSet interface. Clients should not use this class
  318.   directly, but rather the L{StateSet} proxy class.
  319.   
  320.   @param states: Set of states
  321.   @type states: set
  322.   '''
  323.   def __init__(self):
  324.     '''Initializes the state set.'''
  325.     self.states = set()
  326.     
  327.   def contains(self, state):
  328.     '''
  329.     Checks if this StateSet contains the given state.
  330.     
  331.     @param state: State to check
  332.     @type state: Accessibility.StateType
  333.     @return: True if the set contains the given state
  334.     @rtype: boolean
  335.     '''
  336.     return state in self.states
  337.   
  338.   def add(self, state):
  339.     '''
  340.     Adds a state to this set.
  341.     
  342.     @param state: State to add
  343.     @type state: Accessibility.StateType
  344.     '''
  345.     self.states.add(state)
  346.   
  347.   def remove(self, state):
  348.     '''
  349.     Removes a state from this set.
  350.     
  351.     @param state: State to remove
  352.     @type state: Accessibility.StateType
  353.     '''
  354.     self.states.remove(state)
  355.   
  356.   def equals(self, state_set):
  357.     '''
  358.     Checks if this StateSet contains exactly the same members as the given
  359.     StateSet.
  360.     
  361.     @param state_set: Another set
  362.     @type state_set: Accessibility.StateSet
  363.     @return: Are the sets equivalent in terms of their contents?
  364.     @rtype: boolean
  365.     '''
  366.     # don't check private members, object might be from another process
  367.     # or implementation
  368.     return set(state_set.getStates()) == self.states
  369.   
  370.   def compare(self, state_set):
  371.     '''
  372.     Computes the symmetric differences of this L{StateSet} and the given
  373.     L{StateSet}.
  374.  
  375.     @note: This method is not currently implemented because of difficulties
  376.     with reference counting. This method needs to return a new
  377.     Accessibility.StateSet object, but the Python implementation for that
  378.     object needs to be kept alive. The problem is who will keep that
  379.     server implementation alive? As soon as it goes out of scope, it's
  380.     GC'ed. This object cannot keep it alive either as it may go out of
  381.     scope before the new object is ready to be finalized. With a global
  382.     cache of objects, we don't know when to invalidate.
  383.     
  384.     @param state_set: Another set
  385.     @type state_set: Accessibility.StateSet
  386.     @return: Elements in only one of the two sets
  387.     @rtype: Accessibility.StateSet
  388.     '''
  389.     raise ORBit.CORBA.NO_IMPLEMENT
  390.     
  391.     # don't check private members, object might be from another process
  392.     # or implementation
  393.     #states = set(state_set.getStates())
  394.     #diff = self.states.symmetric_difference(states)
  395.     #new_ss = _StateSetImpl()
  396.     #map(new_ss._this().add, diff)
  397.     #return new_ss._this()
  398.   
  399.   def isEmpty(self):
  400.     '''
  401.     Checks if this L{StateSet} is empty.
  402.     
  403.     @return: Is it empty?
  404.     @rtype: boolean
  405.     '''
  406.     return len(self.states) == 0
  407.  
  408.   def getStates(self):
  409.     '''
  410.     Gets the sequence of all states in this set.
  411.     
  412.     @return: List of states
  413.     @rtype: list
  414.     '''
  415.     return list(self.states)
  416.  
  417. class StateSet(object):
  418.   '''
  419.   Python proxy for the L{_StateSetImpl} class. Use this to safely instantiate
  420.   new StateSet objects in Python.
  421.  
  422.   @param impl: State set implementation
  423.   @type impl: L{_StateSetImpl}
  424.   '''
  425.   def __init__(self, *states):
  426.     '''
  427.     Initializes the state set with the given states.
  428.  
  429.     @param states: States to add immediately
  430.     @type states: list
  431.     '''
  432.     self.impl = _StateSetImpl()
  433.     map(self.impl._this().add, states)
  434.     
  435.   def contains(self, state):
  436.     '''
  437.     Checks if this StateSet contains the given state.
  438.     
  439.     @param state: State to check
  440.     @type state: Accessibility.StateType
  441.     @return: True if the set contains the given state
  442.     @rtype: boolean
  443.     '''
  444.     return self.impl._this().contains(state)
  445.   
  446.   def add(self, *states):
  447.     '''
  448.     Adds states to this set.
  449.     
  450.     @param states: State(s) to add
  451.     @type states: Accessibility.StateType
  452.     '''
  453.     map(self.impl._this().add, states)
  454.     
  455.   def remove(self, state):
  456.     '''
  457.     Removes states from this set.
  458.     
  459.     @param states: State(s) to remove
  460.     @type states: Accessibility.StateType
  461.     '''
  462.     map(self.impl._this().remove, state)
  463.   
  464.   def equals(self, state_set):
  465.     '''
  466.     Checks if this StateSet contains exactly the same members as the given
  467.     StateSet.
  468.     
  469.     @param state_set: Another set
  470.     @type state_set: Accessibility.StateSet
  471.     @return: Are the sets equivalent in terms of their contents?
  472.     @rtype: boolean
  473.     '''
  474.     if isinstance(state_set, self.__class__):
  475.       # convenience if we're given a proxy
  476.       state_set = state_set.raw()
  477.     return self.impl._this().equals(state_set)
  478.   
  479.   def compare(self, state_set):
  480.     '''
  481.     Finds the symmetric difference between this state set andthe one provided,
  482.     and returns it as a new StateSet.
  483.  
  484.     @note: This does not use L{_StateSetImpl.compare} which cannot be
  485.     implemented at this time
  486.     @param state_set: Set to compare against
  487.     @type state_set: Accessibility.StateSet
  488.     @return: Proxy for the new set
  489.     @rtype: L{StateSet}
  490.     '''
  491.     if isinstance(state_set, self.__class__):
  492.       # shortcut if it's another one of our proxies
  493.       state_set = state_set.raw()
  494.     a = set(self.impl._this().getStates())
  495.     b = set(state_set.getStates())
  496.     diff = a.symmetric_difference(b)
  497.     return StateSet(*diff)
  498.   
  499.   def isEmpty(self):
  500.     '''
  501.     Checks if this StateSet is empty.
  502.     
  503.     @return: Is it empty?
  504.     @rtype: boolean
  505.     '''
  506.     return self.impl._this().isEmpty()
  507.  
  508.   def getStates(self):
  509.     '''
  510.     Gets the sequence of all states in this set.
  511.     
  512.     @return: List of states
  513.     @rtype: list
  514.     '''
  515.     return self.impl._this().getStates()
  516.  
  517.   def raw(self):
  518.     '''
  519.     Gets the Accessibility.StateSet object proxied for use in a remote
  520.     call.
  521.  
  522.     @return: State set
  523.     @rtype: Accessibility.StateSet
  524.     '''
  525.     return self.impl._this()
  526.